import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import Icon from '../icon';
import { canUseDom } from '../utils/detect';
import Events from '../utils/events';
import { noop } from '../utils/misc';
import {
	ToastComponent,
	ToastExportExtra,
	ToastInstance,
	ToastProps,
} from './interface';
import CToast from './Toast';

let ALLOW_MULTIPLE = false;

let INSTANCES: ToastInstance[] = [];

function createToast(baseProps: ToastProps): ToastInstance {
	if (!canUseDom()) {
		return {
			update: noop,
			clear: noop,
		};
	}

	const events = new Events();

	let cleared = false;
	const container = document.createElement('div');
	document.body.append(container);

	const instance: ToastInstance = {
		update: (props) => {
			events.emit('change', props);
		},
		clear: () => {
			if (!cleared) {
				const nextProps: ToastProps = { visible: false };
				events.emit('change', nextProps);

				INSTANCES = INSTANCES.filter((i) => i !== instance);
				cleared = true;
			}
		},
	};

	const ToastStatic: React.FC<ToastProps> = () => {
		const [props, setProps] = useState(() => baseProps);

		useEffect(() => {
			const listener = (next: ToastProps) => {
				setProps((prev) => ({ ...prev, ...next }));
			};

			events.on('change', listener);

			return () => {
				events.off('change', listener);
			};
		}, []);

		return (
			<CToast
				visible={true}
				{...props}
				mountTo={false}
				onAfterHide={() => {
					instance.clear();

					setTimeout(() => {
						ReactDOM.unmountComponentAtNode(container);
						document.body.removeChild(container);
					}, 0);

					if (props.onAfterHide) {
						props.onAfterHide();
					}
				}}
			/>
		);
	};

	ToastStatic.displayName = 'Toast';

	ReactDOM.render(<ToastStatic />, container);
	INSTANCES.push(instance);

	return instance;
}

const Toast: ToastComponent & ToastExportExtra = CToast as any;

function clear() {
	INSTANCES.forEach((inst) => {
		inst.clear();
	});
}

function show(message: string | ToastProps, defaults: ToastProps = {}) {
	const props: ToastProps =
		typeof message === 'string' ? { message } : message;

	if (!ALLOW_MULTIPLE) {
		Toast.clear();
	}

	return createToast({ ...defaults, ...props });
}

Toast.show = (message) => show(message);

Toast.loading = (message) =>
	show(message, { icon: <Icon.Spinner />, forbidClick: true });

Toast.success = (message) => show(message, { icon: <Icon name="success" /> });

Toast.fail = (message) => show(message, { icon: <Icon name="fail" /> });

Toast.clear = clear;

Toast.allowMultiple = (allow) => {
	ALLOW_MULTIPLE = !!allow;
};

export type {
	ToastComponent,
	ToastInstance,
	ToastPosition,
	ToastProps,
} from './interface';
export default Toast;
